﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using Kugar.Core.BaseStruct;
using Kugar.Core.ExtMethod;
using Kugar.Core.Log;
using Kugar.Core.Network;
using Kugar.Core.Web;
using Kugar.Web.Wechat.Entities;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Formatting = Newtonsoft.Json.Formatting;

namespace Kugar.Web.Wechat
{
    public interface IWechatPay
    {
        ResultReturn<JObject> CreateAppPay(
            string appID,
            string payKey,
            string mchID,
            string body,
            decimal amount,
            string orderCode,
            string clientIP,
            string state="",
            string host = "");

        /// <summary>
        /// 返回公众号支付所需的参数
        /// </summary>
        /// <returns></returns>
        ResultReturn<JObject> CreateJSApiPay(
            string appID,
            string payKey,
            string mchID,
            string orderCode,
            decimal payMoney, 
            string body,
            string openID,
            string clientIP,
            string state="",
            string host="");

        /// <summary>
        /// 生成支付二维码
        /// </summary>
        /// <param name="appID"></param>
        /// <param name="payKey"></param>
        /// <param name="mchID"></param>
        /// <param name="orderCode"></param>
        /// <param name="payMoney"></param>
        /// <param name="body"></param>
        /// <param name="clientIP"></param>
        /// <param name="state"></param>
        /// <param name="host"></param>
        /// <returns></returns>
        ResultReturn<string> CreateQrCode(
            string appID,
            string payKey,
            string mchID, 
            string orderCode, 
            decimal payMoney, 
            string body,
            string clientIP,
            string state = "",
            string host = "");

        /// <summary>
        /// 刷卡支付,扫扫码支付
        /// </summary>
        /// <param name="appID"></param>
        /// <param name="payKey"></param>
        /// <param name="mchID"></param>
        /// <param name="orderCode"></param>
        /// <param name="payMoney"></param>
        /// <param name="body"></param>
        /// <param name="clientIP"></param>
        /// <param name="auth_Code">刷卡支付的授权码</param>
        /// <returns></returns>
        Task<ResultReturn<(string transaction_id,int state)>> CreateMicroPay(
            string appID,
            string payKey,
            string mchID, 
            string orderCode,
            decimal payMoney, 
            string body,
            string clientIP,
            string auth_Code
        );

        /// <summary>
        /// 发送一个红包给用户
        /// </summary>
        /// <param name="appID"></param>
        /// <param name="payKey"></param>
        /// <param name="mchID"></param>
        /// <param name="certificateData"></param>
        /// <param name="orderCode"></param>
        /// <param name="senderName"></param>
        /// <param name="title"></param>
        /// <param name="activityName"></param>
        /// <param name="userOpenID"></param>
        /// <param name="money"></param>
        /// <param name="clientIP"></param>
        /// <returns></returns>
        ResultReturn<string> SendRedpackage(string appID,
            string payKey,
            string mchID,
            byte[] certificateData,
            string orderCode,
            string senderName,
            string title,
            string activityName,
            string userOpenID,
            decimal money,
            string clientIP,
            string scscene_id = ""
        );

        /// <summary>
        /// 发送一个红包到用户零钱中
        /// </summary>
        /// <param name="appID">商户账号appid</param>
        /// <param name="payKey">支付秘钥</param>
        /// <param name="mchID">商户号</param>
        /// <param name="certificateData"></param>
        /// <param name="orderCode">订单号</param>
        /// <param name="userOpenID">收款用户OpenID</param>
        /// <param name="money">红包金额</param>
        /// <param name="comment">企业付款备注</param>
        /// <param name="clientIP">客户端IP</param>
        /// <param name="isForceCheckName">是否强制检查实名</param>
        /// <param name="userRealName">接收红包的用户实名,如果isForceCheckName=true,,该参数必填</param>
        /// <returns></returns>
        ResultReturn<string> SendRedpackToCash(string appID,
            string payKey,
            string mchID,
            byte[] certificateData,
            string orderCode,

            string userOpenID,
            decimal money,
            string comment,
            string clientIP,

            bool isForceCheckName = false,
            string userRealName = ""
        );

        ResultReturn<string> Query(string appID, string mchID, string payKey, string orderCode);

        /// <summary>
        /// 订单退单,资金原路返回
        /// </summary>
        /// <param name="payOAuthID"></param>
        /// <param name="transaction_id"></param>
        /// <param name="refundOrder"></param>
        /// <param name="payMoney"></param>
        /// <param name="refundMoney"></param>
        /// <returns></returns>
        ResultReturn<string> Refund(string appID, 
            string mchID, 
            string payKey, 
            byte[] certificateData,
            string transaction_id, 
            string refundOrder,
            decimal payMoney, 
            decimal refundMoney,
            string refund_account=""
        );

        /// <summary>
        /// 取消订单
        /// </summary>
        /// <param name="payOAuthID"></param>
        /// <param name="orderCode"></param>
        /// <returns></returns>
        ResultReturn Cancel(string appID,
            string mchID,
            string payKey,
            byte[] certificateData, string orderCode);

        /*/// <summary>
        /// 支付成功后,微支付通知服务器,处理后,调用该事件
        /// </summary>
        event PaySuccessNotifyHandler PaySuccessNotify;

        /// <summary>
        /// 支付失败后通知
        /// </summary>
        event PayFailNotifyHandler PayFailNotify;

        /// <summary>
        /// 需要支付key时,触发该事件,在回调时,需要校验回调数据时,需要paykey
        /// </summary>
        event PayKeyRequestHandler RequestPayKey;

        /// <summary>
        /// 该事件用于每次触发回调时,记录回调实际xml内容,做日志记录用
        /// </summary>
        event PayNotyfyLog NotifyLog;*/
    }

    public delegate void PayNotyfyLog(Microsoft.AspNetCore.Http.HttpContext context, string xml);

    public delegate void PaySuccessNotifyHandler(string appID, string mchID, string openID, decimal amount, string orderCode,
        string transaction_id, string state);

    public delegate string PayKeyRequestHandler(string appID, string mchID);

    public delegate int PayFailNotifyHandler(string msg, XmlDocument xmlDoc);

    public class WechatPay : IWechatPay
    {
        private  static readonly WechatPay _defaultValue = new WechatPay();

        private WechatPay() { }

        public ResultReturn<JObject> CreateAppPay(
            string appID,
            string payKey,
            string mchID,
            string body,
            decimal amount,
            string orderCode,
            string clientIP,
            string state="",
            string host = "")
        {
            if (string.IsNullOrEmpty(host))
            {
                host = HttpContext.Current.Request.Host.Host;
            }

            var notifyurl = $"http://{host}/Core/MP/Pay/Notify";

            //var appID = oauth.Item3.ExtInfo.Get<string>("AppID");
            //var partnerID = oauth.Item3.ExtInfo.Get<string>("MchID");
            //var key = oauth.Item3.ExtInfo.Get<string>("PayKey");

            WxPayData data = new WxPayData();
            data.SetValue("body", body); //商品描述
            data.SetValue("attach", new JObject(){["State"] =state.ToStringEx()}.ToStringEx(Formatting.None)); //附加数据
            data.SetValue("out_trade_no", orderCode + '_' + RandomEx.Next(100, 999)); //随机字符串
            data.SetValue("total_fee", ((int) (amount * 100)).ToString()); //总金额
            data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss")); //交易起始时间
            data.SetValue("time_expire", DateTime.Now.AddMinutes(20).ToString("yyyyMMddHHmmss")); //交易结束时间
            //data.SetValue("goods_tag", orderid);//商品标记
            data.SetValue("trade_type", "APP"); //交易类型
            data.SetValue("product_id", orderCode); //商品ID
            data.SetValue("notify_url", notifyurl);
            data.SetValue("spbill_create_ip", clientIP);
            data.SetValue("appid", appID);
            data.SetValue("mch_id", mchID);

            WxPayData result = MP_UnifiedOrder(data, payKey); //调用统一下单接口

            var appdata = new WxPayData();

            appdata.SetValue("appid", appID);
            appdata.SetValue("noncestr", GenerateNonceStr());
            appdata.SetValue("prepayid", result.GetValue("prepay_id").ToStringEx());
            appdata.SetValue("package", "Sign=WXPay");
            appdata.SetValue("partnerid", mchID);
            appdata.SetValue("timestamp", GenerateTimeStamp());
            appdata.SetValue("sign", appdata.MakeSign(payKey));

            var json = new JObject()
                       {
                           ["appid"] = appID,
                           ["noncestr"] = appdata.GetValue("noncestr").ToStringEx(),
                           ["sign"] =
                               appdata.GetValue("sign").ToStringEx(), //result.GetValue("sign").ToStringEx().ToLower(),
                           //["prepay_id"]=result.GetValue("prepay_id").ToStringEx().ToStringEx().ToLower(),
                           ["package"] = "Sign=WXPay",
                           ["partnerid"] = mchID,
                           ["prepayid"] = appdata.GetValue("prepayid").ToStringEx(),
                           ["timestamp"] =
                               appdata.GetValue("timestamp").ToStringEx(), //;//data.GetValue("timestamp").ToStringEx()
                       };

            return new SuccessResultReturn<JObject>(json);
        }

        /// <summary>
        /// 返回公众号支付所需的参数
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        ///  该函数只返回调起公众号微支付所需的参数,实际调用请参考:<br/>
        /// https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
        /// </remarks>
        public ResultReturn<JObject> CreateJSApiPay(
            string appID,
            string payKey,
            string mchID,
            string orderCode,
            decimal payMoney, 
            string body,
            string openID,
            string clientIP,
            string state="",
            string host="")
        {
            if (string.IsNullOrEmpty(host))
            {
                host = HttpContext.Current.Request.Host.Host;
            }

            var notifyurl = $"http://{host}/Core/MP/Pay/Notify";
            
            var timeStamp = GenerateTimeStamp();
            var nonceStr = GenerateNonceStr();

            //统一下单
            WxPayData prepayData = new WxPayData();
            prepayData.SetValue("body", body);
            prepayData.SetValue("appid", appID);
            prepayData.SetValue("mch_id", mchID);
            prepayData.SetValue("nonce_str", nonceStr); //随机字符串
            prepayData.SetValue("attach", new JObject() { ["State"] = state.ToStringEx() }.ToStringEx(Formatting.None));
            prepayData.SetValue("out_trade_no", orderCode);
            prepayData.SetValue("spbill_create_ip", clientIP);
            prepayData.SetValue("total_fee", (payMoney * 100).ToString("f0"));
            prepayData.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));
            prepayData.SetValue("time_expire", DateTime.Now.AddMinutes(30).ToString("yyyyMMddHHmmss"));
            prepayData.SetValue("goods_tag", body);
            prepayData.SetValue("trade_type", "JSAPI");
            prepayData.SetValue("notify_url", notifyurl);
            prepayData.SetValue("openid", openID);

            var ret = MP_UnifiedOrder(prepayData, payKey, 6);

            var prepay_id= ret.GetValue("prepay_id").ToStringEx();

            if (string.IsNullOrWhiteSpace(prepay_id))
            {
                LoggerManager.Default.Error("调用支付接口错误: \n" + ret.ToXml());
                return new FailResultReturn<JObject>(ret.GetValue("return_msg").ToStringEx());
            }
            

            WxPayData jsApiParam = new WxPayData();
            jsApiParam.SetValue("appId", appID);
            jsApiParam.SetValue("timeStamp", timeStamp);
            jsApiParam.SetValue("nonceStr", nonceStr);
            jsApiParam.SetValue("package", "prepay_id=" + prepay_id);
            jsApiParam.SetValue("signType", "MD5");
            jsApiParam.SetValue("paySign", jsApiParam.MakeSign(payKey));

            var jobj = new JObject()
            {
                ["appId"] = appID,
                ["timeStamp"] = timeStamp,
                ["nonceStr"] = nonceStr,
                ["package"] = "prepay_id=" + prepay_id,
                ["signType"] = "MD5",
                ["paySign"] = jsApiParam.GetValue("paySign").ToStringEx() // jsApiParam.MakeSign(key)
            };
            
            return new SuccessResultReturn<JObject>(jobj);
        }

        /// <summary>
        /// 生成支付二维码
        /// </summary>
        /// <param name="appID"></param>
        /// <param name="payKey"></param>
        /// <param name="mchID"></param>
        /// <param name="orderCode"></param>
        /// <param name="payMoney"></param>
        /// <param name="body"></param>
        /// <param name="clientIP"></param>
        /// <param name="state"></param>
        /// <param name="host"></param>
        /// <returns></returns>
        public ResultReturn<string> CreateQrCode(
            string appID,
            string payKey,
            string mchID, 
            string orderCode, 
            decimal payMoney, 
            string body,
            string clientIP,
            string state = "",
            string host = "")
        {
            if (string.IsNullOrWhiteSpace(orderCode))
            {
                return new FailResultReturn<string>("订单号不能为空");
            }

            if (payMoney <= 0)
            {
                return new FailResultReturn<string>("付款金额不能小于0");
            }

            if (string.IsNullOrWhiteSpace(body))
            {
                return new FailResultReturn<string>("body不能为空");
            }

            WxPayData data = new WxPayData();

            if (string.IsNullOrEmpty(host))
            {
                host = HttpContext.Current.Request.Host.Host;
            }

            var notifyurl = $"http://{host}/Core/MP/Pay/Notify";


            data.SetValue("body", body); //商品描述
            data.SetValue("attach",new JObject(){["State"]=state.ToStringEx()}.ToStringEx(Formatting.None)); //附加数据
            data.SetValue("out_trade_no", orderCode + '_' + RandomEx.Next(100, 999)); //随机字符串
            data.SetValue("total_fee", (payMoney * 100).ToString("f0")); //总金额
            data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss")); //交易起始时间
            data.SetValue("time_expire", DateTime.Now.AddMinutes(30).ToString("yyyyMMddHHmmss")); //交易结束时间
            data.SetValue("goods_tag", body); //商品标记
            data.SetValue("trade_type", "NATIVE"); //交易类型
            data.SetValue("product_id", orderCode); //商品ID
            data.SetValue("spbill_create_ip", clientIP);

            data.SetValue("notify_url", notifyurl);
            data.SetValue("appid", appID);
            data.SetValue("mch_id", mchID);
            
            WxPayData result = MP_UnifiedOrder(data, payKey); //调用统一下单接口

            string url = result.GetValue("code_url").ToString(); //获得统一下单接口返回的二维码链接
            
            return new SuccessResultReturn<string>(url);

        }

        /// <summary>
        /// 刷卡支付,扫扫码支付
        /// </summary>
        /// <param name="appID"></param>
        /// <param name="payKey"></param>
        /// <param name="mchID"></param>
        /// <param name="orderCode"></param>
        /// <param name="payMoney"></param>
        /// <param name="body"></param>
        /// <param name="clientIP"></param>
        /// <param name="auth_Code">刷卡支付的授权码</param>
        /// <returns></returns>
        public async Task<ResultReturn<(string transaction_id,int state)>> CreateMicroPay(
            string appID,
            string payKey,
            string mchID, 
            string orderCode,
            decimal payMoney, 
            string body,
            string clientIP,
            string auth_Code
        )
        {
            const string url = "https://api.mch.weixin.qq.com/pay/micropay";
            
            var inputObj = new WxPayData();

            inputObj.SetValue("auth_code", auth_Code); //授权码
            inputObj.SetValue("body", body); //商品描述
            inputObj.SetValue("total_fee", (payMoney * 100).ToString("f0")); //总金额
            inputObj.SetValue("out_trade_no", orderCode); //产生随机的商户订单号
            inputObj.SetValue("spbill_create_ip", clientIP); //终端ip
            inputObj.SetValue("appid", appID); //公众账号ID
            inputObj.SetValue("mch_id", mchID); //商户号
            inputObj.SetValue("nonce_str", GenerateNonceStr()); //随机字符串
            inputObj.SetValue("sign", inputObj.MakeSign(payKey)); //签名
            string xml = inputObj.ToXml();
            
            var response = Post(url, xml, null, "", 6); ;
            
            var result=new WxPayData();
            result.FromXml(response, payKey);

            //如果提交被扫支付接口调用失败，则抛异常
            if (!result.IsSet("return_code") || result.GetValue("return_code").ToString() == "FAIL")
            {
                string returnMsg = result.IsSet("return_msg") ? result.GetValue("return_msg").ToString() : "";
 
                return new FailResultReturn<(string transaction_id, int state)> ($"支付失败=刷卡支付接口调用失败:{returnMsg}");
            }

            //刷卡支付直接成功
            if (result.GetValue("return_code").ToString() == "SUCCESS" &&
                result.GetValue("result_code").ToString() == "SUCCESS")
            {
                return new SuccessResultReturn<(string transaction_id, int state)>((result.GetValue("transaction_id").ToStringEx(),0));
            }

            if (result.GetValue("err_code").ToString() == "USERPAYING")
            {
                return new ResultReturn<(string transaction_id, int state)>(false, ("", 1), returnCode:100);
            }

            ///******************************************************************
            // * 剩下的都是接口调用成功，业务失败的情况
            // * ****************************************************************/
            ////1）业务结果明确失败
            //if (result.GetValue("err_code").ToString() != "USERPAYING" &&
            //    result.GetValue("err_code").ToString() != "SYSTEMERROR")
            //{
            //    return new FailResultReturn<(string transaction_id, int state)>($"支付失败=支付接口调用失败2:{result.ToXml()}");
            //}


            ////2）不能确定是否失败，需查单
            ////用商户订单号去查单
            ////string out_trade_no = result.GetValue("out_trade_no").ToString();

            ////确认支付是否成功,每隔一段时间查询一次订单，共查询10次
            //int queryTimes = 5; //查询次数计数器
            //while (queryTimes-- > 0)
            //{
            //    var ret = Query(appID, mchID, payKey, orderCode);
                
            //    if (ret.IsSuccess)
            //    {
            //        return new SuccessResultReturn<(string transaction_id, int state)>((ret.ReturnData,0));
            //    }
            //    else if (!ret.IsSuccess && ret.ReturnCode == 2)
            //    {
            //        Thread.Sleep(500);
            //        //Task.Delay(2000);
            //    }
            //    else
            //    {
            //        return new FailResultReturn<(string transaction_id, int state)>("支付失败,等待用户输入密码");
            //    }

            //}

            return new FailResultReturn<(string transaction_id, int state)>("支付失败,等待用户输入密码");
            
        }

        /// <summary>
        /// 发送一个公众号红包给用户
        /// </summary>
        /// <param name="appID"></param>
        /// <param name="payKey"></param>
        /// <param name="mchID"></param>
        /// <param name="certificateData"></param>
        /// <param name="orderCode"></param>
        /// <param name="senderName"></param>
        /// <param name="title"></param>
        /// <param name="activityName"></param>
        /// <param name="userOpenID"></param>
        /// <param name="money"></param>
        /// <param name="clientIP"></param>
        /// <returns></returns>
        public ResultReturn<string> SendRedpackage(string appID,
            string payKey,
            string mchID,
            byte[] certificateData,
            string orderCode,
            string senderName,
            string title,
            string activityName,
            string userOpenID,
            decimal money,
            string clientIP,
            string scscene_id=""
            )
        {
            const string url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
            
            WxPayData data = new WxPayData();
            data.SetValue("wxappid", appID);
            data.SetValue("mch_id", mchID);
            data.SetValue("mch_billno", orderCode);
            data.SetValue("re_openid", userOpenID);
            data.SetValue("send_name", senderName);
            data.SetValue("total_amount", ((int)(money * 100)).ToString());
            data.SetValue("total_num", 1);
            data.SetValue("wishing", title);
            data.SetValue("act_name", activityName);
            data.SetValue("client_ip", clientIP);
            data.SetValue("remark", "");
            data.SetValue("nonce_str", GenerateNonceStr()); //随机字符串

            if (!string.IsNullOrWhiteSpace(scscene_id))
            {
                data.SetValue("scscene_id", scscene_id);
            }

            data.SetValue("sign", data.MakeSign(payKey)); //签名

            string xml = data.ToXml();
            
            ResultReturn<string> ret = null;

            var requestData = "";
            var responseData = "";
            var payTransactionID = "";

            var response = Post(xml, url, certificateData, mchID, 6);

            WxPayData result = new WxPayData();

            result.FromXml(response, payKey);

            if (result.GetValue("return_code").ToStringEx() == "SUCCESS" && result.GetValue("result_code").ToStringEx() == "SUCCESS")
            {
                payTransactionID = result.GetValue("send_listid").ToStringEx();

                ret = new SuccessResultReturn<string>(payTransactionID);
            }
            else
            {
                var return_Msg = result.GetValue("return_msg").ToStringEx();
                var err_Code = result.GetValue("err_code").ToStringEx();
                var err_code_des = result.GetValue("err_code_des").ToStringEx();

                ret = new FailResultReturn<string>(string.IsNullOrWhiteSpace(err_code_des) ? return_Msg : err_code_des);
                
            }

            return ret;
        }

        /// <summary>
        /// 发送一个红包到用户零钱中
        /// </summary>
        /// <param name="appID">商户账号appid</param>
        /// <param name="payKey">支付秘钥</param>
        /// <param name="mchID">商户号</param>
        /// <param name="certificateData"></param>
        /// <param name="orderCode">订单号</param>
        /// <param name="userOpenID">收款用户OpenID</param>
        /// <param name="money">红包金额</param>
        /// <param name="comment">企业付款备注</param>
        /// <param name="clientIP">客户端IP</param>
        /// <param name="isForceCheckName">是否强制检查实名</param>
        /// <param name="userRealName">接收红包的用户实名,如果isForceCheckName=true,,该参数必填</param>
        /// <returns></returns>
        public ResultReturn<string> SendRedpackToCash(string appID,
            string payKey,
            string mchID,
            byte[] certificateData,
            string orderCode,
            
            string userOpenID,
            decimal money,
            string comment,
            string clientIP,
            
            bool isForceCheckName=false,
            string userRealName=""
            )
        {
            if (string.IsNullOrEmpty(comment) || comment.Length==0)
            {
                throw new ArgumentOutOfRangeException(nameof(comment), "付款备注必填");
            }

            if (comment.Length>100)
            {
                throw new ArgumentOutOfRangeException(nameof(comment),"付款备注不能超过100个字符");
            }

            if (isForceCheckName)
            {
                if (string.IsNullOrEmpty(userRealName) || userRealName.Length==0 || userRealName.Length>64)
                {
                    throw new ArgumentOutOfRangeException(nameof(userRealName),"实名名称必填,并且不能超过64个字符");
                }
            }

            const string url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";

            WxPayData data = new WxPayData();
            data.SetValue("mch_appid", appID);
            data.SetValue("mchid", mchID);
            data.SetValue("nonce_str", GenerateNonceStr()); //随机字符串
            data.SetValue("partner_trade_no", orderCode); 

            data.SetValue("openid", userOpenID);
            data.SetValue("amount", ((int)(money * 100)).ToString());
            data.SetValue("desc", comment);

            data.SetValue("spbill_create_ip", clientIP);
            
            if (isForceCheckName)
            {
                data.SetValue("check_name", "FORCE_CHECK");
                data.SetValue("re_user_name", userRealName);
            }
            else
            {
                data.SetValue("check_name", "NO_CHECK");
            }

            data.SetValue("sign", data.MakeSign(payKey)); //签名

            string xml = data.ToXml();

            LoggerManager.Default.Debug("支付零钱xml:" + xml);

            ResultReturn<string> ret = null;

            var requestData = "";
            var responseData = "";
            var payTransactionID = "";

            var response = Post(xml, url, certificateData, mchID, 6);

            WxPayData result = new WxPayData();

            result.FromXml(response, payKey);

            if (result.GetValue("return_code").ToStringEx() == "SUCCESS" && result.GetValue("result_code").ToStringEx() == "SUCCESS")
            {
                payTransactionID = result.GetValue("payment_no").ToStringEx();

                ret = new SuccessResultReturn<string>(payTransactionID);
            }
            else
            {
                var return_Msg = result.GetValue("return_msg").ToStringEx();
                var err_Code = result.GetValue("err_code").ToStringEx();
                var err_code_des = result.GetValue("err_code_des").ToStringEx();

                ret = new FailResultReturn<string>(string.IsNullOrWhiteSpace(err_code_des) ? return_Msg : err_code_des);

            }

            return ret;
        }

        public ResultReturn<string> Query(string appID, string mchID, string payKey, string orderCode)
        {
            string url = "https://api.mch.weixin.qq.com/pay/orderquery";
            //检测必填参数

            var inputObj = new WxPayData();

            inputObj.SetValue("appid", appID); //公众账号ID
            inputObj.SetValue("mch_id", mchID); //商户号
            inputObj.SetValue("out_trade_no", orderCode);
            inputObj.SetValue("nonce_str", GenerateNonceStr()); //随机字符串
            inputObj.SetValue("sign", inputObj.MakeSign(payKey)); //签名

            string xml = inputObj.ToXml();

            var response = Post(url, xml,null,"",6);

            //将xml格式的数据转化为对象以返回
            WxPayData result =new WxPayData();

            result.FromXml(response, payKey);
    
            if (result.GetValue("return_code").ToString() == "SUCCESS"
                && result.GetValue("result_code").ToString() == "SUCCESS")
            {
                //支付成功
                if (result.GetValue("trade_state").ToString() == "SUCCESS")
                {
                    return new SuccessResultReturn<string>(result.GetValue("transaction_id").ToStringEx());
                }
                //用户支付中，需要继续查询
                else if (result.GetValue("trade_state").ToString() == "USERPAYING")
                {
                    return new FailResultReturn<string>("未支付完成,请等待")
                    {
                        ReturnCode = 2
                    };
                }
            }

            //如果返回错误码为“此交易订单号不存在”则直接认定失败
            if (result.GetValue("err_code", "").ToStringEx() == "ORDERNOTEXIST")
            {
                return new FailResultReturn<string>(result.GetValue("return_msg").ToString())
                {
                    ReturnCode = 99
                };
            }
            else
            {
                return new FailResultReturn<string>("其他系统错误", 98);
            }
        }

        /// <summary>
        /// 订单退单,资金原路返回
        /// </summary>
        /// <param name="payOAuthID"></param>
        /// <param name="transaction_id"></param>
        /// <param name="refundOrder"></param>
        /// <param name="payMoney"></param>
        /// <param name="refundMoney"></param>
        /// <returns></returns>
        public ResultReturn<string> Refund(string appID, 
            string mchID, 
            string payKey, 
            byte[] certificateData,
            string transaction_id, 
            string refundOrder,
            decimal payMoney, 
            decimal refundMoney,
            string refund_account=""
            )
        {

            const string url = "https://api.mch.weixin.qq.com/secapi/pay/refund";

            var inputObj = new WxPayData();
            inputObj.SetValue("transaction_id", transaction_id);
            inputObj.SetValue("total_fee", (payMoney * 100).ToString("f0")); //订单总金额
            inputObj.SetValue("refund_fee", (refundMoney * 100).ToString("f0")); //退款金额
            inputObj.SetValue("out_refund_no", refundOrder); //随机生成商户退款单号
            inputObj.SetValue("op_user_id", mchID); //操作员，默认为商户号

            if (!string.IsNullOrEmpty(refund_account))
            {
                inputObj.SetValue("refund_account", refund_account);
            }

            inputObj.SetValue("appid", appID); //公众账号ID
            inputObj.SetValue("mch_id", mchID); //商户号
            inputObj.SetValue("nonce_str", GenerateNonceStr()); //随机字符串
            inputObj.SetValue("sign", inputObj.MakeSign(payKey)); //签名

            string xml = inputObj.ToXml();

            string response = "";

            try
            {
                response = Post(xml, url, certificateData, mchID, 6);
            }
            catch (Exception ex)
            {
                return new FailResultReturn<string>(ex);
            }

            WxPayData result = new WxPayData();
            result.FromXml(response, payKey);
            
            if (result.GetValue("return_code").ToStringEx() == "SUCCESS" &&
                result.GetValue("result_code").ToStringEx() == "SUCCESS")
            {

                return new SuccessResultReturn<string>(result.GetValue("refund_id").ToStringEx());
            }
            else
            {
                if (result.GetValue("err_code").ToStringEx() == "NOTENOUGH")
                {
                    var result1 = Refund(appID, mchID, payKey, certificateData, transaction_id, refundOrder, payMoney, refundMoney,refund_account: "REFUND_SOURCE_RECHARGE_FUNDS");

                    return result1;
                }

                return
                    new FailResultReturn<string>(
                        $"退款错误:{result.GetValue("return_msg").ToStringEx()},{result.GetValue("err_code_des").ToStringEx()}");
            }
        }

        /// <summary>
        /// 取消订单
        /// </summary>
        /// <param name="payOAuthID"></param>
        /// <param name="orderCode"></param>
        /// <returns></returns>
        public ResultReturn Cancel(string appID,
            string mchID,
            string payKey,
            byte[] certificateData, string orderCode)
        {
            const string url = "https://api.mch.weixin.qq.com/secapi/pay/reverse";

            WxPayData inputObj = new WxPayData();

            inputObj.SetValue("out_trade_no", orderCode);
            inputObj.SetValue("appid", appID); //公众账号ID
            inputObj.SetValue("mch_id", mchID); //商户号
            inputObj.SetValue("nonce_str", GenerateNonceStr()); //随机字符串
            inputObj.SetValue("sign", inputObj.MakeSign(payKey)); //签名

            string xml = inputObj.ToXml();
            
            var response = Post(xml, url, certificateData, mchID, 6);
            
            WxPayData result = new WxPayData();

            result.FromXml(response, payKey);

            //接口调用失败
            if (result.GetValue("return_code").ToString() != "SUCCESS")
            {
                return new FailResultReturn(result.GetValue("return_msg").ToStringEx());
            }

            //如果结果为success且不需要重新调用撤销，则表示撤销成功
            if (result.GetValue("result_code").ToString() != "SUCCESS" && result.GetValue("recall").ToString() == "N")
            {
                return SuccessResultReturn.Default;
            }
            else if (result.GetValue("recall").ToString() == "Y")
            {
                return new FailResultReturn("请重新调用取消");
            }

            return new FailResultReturn("调用失败:" + result.GetValue("return_msg").ToStringEx());
        }

        /*public event PaySuccessNotifyHandler PaySuccessNotify;
        public event PayFailNotifyHandler PayFailNotify;
        public event PayKeyRequestHandler RequestPayKey;
        public event PayNotyfyLog NotifyLog;*/


        //public static WechatPay Default => _defaultValue;



        /*internal void OnPaySuccessNotify(string appID, string mchID, string openID, decimal amount, string orderCode,
            string transaction_id, string state)
        {
            if (PaySuccessNotify!=null)
            {
                PaySuccessNotify(appID, mchID, openID, amount, orderCode, transaction_id, state);
            }
            
        }

        internal void OnPayFailNotify(string msg, XmlDocument xmlDoc)
        {
            if (PayFailNotify!=null)
            {
                PayFailNotify(msg, xmlDoc);
            }
            
        }

        internal string OnPayKeyRequest(string appID, string mchID)
        {
            if (RequestPayKey==null)
            { 
                LoggerManager.Default.Error("回调触发时,支付未返回");
                return string.Empty;
            }
            return RequestPayKey(appID, mchID);
        }

        internal void OnNotifyLog(Microsoft.AspNetCore.Http.HttpContext context, string xml)
        {
            if (NotifyLog!=null)
            {
                NotifyLog(context, xml);
            }
            
        }*/

        /**
        * 生成时间戳，标准北京时间，时区为东八区，自1970年1月1日 0点0分0秒以来的秒数
        * @return 时间戳
        */
        private string GenerateTimeStamp()
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalSeconds).ToString();
        }

        /**
        * 生成随机串，随机串包含字母或数字
        * @return 随机串
        */
        private string GenerateNonceStr()
        {
            return Guid.NewGuid().ToString().Replace("-", "").ToLower();
        }

        /**
        * 
        * 统一下单
        * @param WxPaydata inputObj 提交给统一下单API的参数
        * @param int timeOut 超时时间
        * @throws WxPayException
        * @return 成功时返回，其他抛异常
        */
        private WxPayData MP_UnifiedOrder(WxPayData inputObj, string key, int timeOut = 6)
        {
            string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

            //检测必填参数
            if (!inputObj.IsSet("out_trade_no"))
            {
                throw new Exception("缺少统一支付接口必填参数out_trade_no！");
            }
            else if (!inputObj.IsSet("body"))
            {
                throw new Exception("缺少统一支付接口必填参数body！");
            }
            else if (!inputObj.IsSet("total_fee"))
            {
                throw new Exception("缺少统一支付接口必填参数total_fee！");
            }
            else if (!inputObj.IsSet("trade_type"))
            {
                throw new Exception("缺少统一支付接口必填参数trade_type！");
            }

            //关联参数
            if (inputObj.GetValue("trade_type").ToString() == "JSAPI" && !inputObj.IsSet("openid"))
            {
                throw new Exception("统一支付接口中，缺少必填参数openid！trade_type为JSAPI时，openid为必填参数！");
            }

            if (inputObj.GetValue("trade_type").ToString() == "NATIVE" && !inputObj.IsSet("product_id"))
            {
                throw new Exception("统一支付接口中，缺少必填参数product_id！trade_type为JSAPI时，product_id为必填参数！");
            }

            //异步通知url未设置，则使用配置文件中的url
            //if (!inputObj.IsSet("notify_url"))
            //{
            //    inputObj.SetValue("notify_url", WxPayConfig.NOTIFY_URL);//异步通知url
            //}

            //inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
            //inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
            //inputObj.SetValue("spbill_create_ip", WxPayConfig.IP);//终端ip	  	    
            //inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串

            //签名
            inputObj.SetValue("sign", inputObj.MakeSign(key));
            string xml = inputObj.ToXml();

            var start = DateTime.Now;

            //Log.Debug("WxPayApi", "UnfiedOrder request : " + xml);
            string response = Post(xml, url, null, "", 6);
            //string response = WebHelper.GetUriDataByPost(url, xml);// HttpService.Post(xml, url, false, timeOut);
            //Log.Debug("WxPayApi", "UnfiedOrder response : " + response);

            var end = DateTime.Now;
            int timeCost = (int) ((end - start).TotalMilliseconds);

            WxPayData result = new WxPayData();
            result.FromXml(response, key);

            //ReportCostTime(url, timeCost, result);//测速上报

            return result;
        }
        
        private string Post(string xml, string url, byte[] certData, string ssl_Password, int timeout)
        {
            string result = "";//返回结果

            HttpWebRequest request = null;
            HttpWebResponse response = null;
            Stream reqStream = null;

            try
            {
                //设置最大连接数


                /***************************************************************
                * 下面设置HttpWebRequest的相关属性
                * ************************************************************/
                request = (HttpWebRequest)WebRequest.Create(url);

                request.Method = "POST";
                request.Timeout = timeout * 1000;

                //设置代理服务器
                //WebProxy proxy = new WebProxy();                          //定义一个网关对象
                //proxy.Address = new Uri(WxPayConfig.PROXY_URL);              //网关服务器端口:端口
                //request.Proxy = proxy;

                //设置POST的数据类型和长度
                request.ContentType = "text/xml";
                request.TransferEncoding = "utf-8";
                byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
                request.ContentLength = data.Length;

                //是否使用证书
                if (certData.HasData() && !string.IsNullOrWhiteSpace(ssl_Password))
                {
                    //string path = HttpContext.Current.Request.PhysicalApplicationPath;
                    X509Certificate2 cert = new X509Certificate2(certData, ssl_Password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);

                    request.ClientCertificates.Add(cert);
                }

                //往服务器写入数据
                reqStream = request.GetRequestStream();
                reqStream.Write(data, 0, data.Length);
                reqStream.Close();

                //获取服务端返回
                response = (HttpWebResponse)request.GetResponse();

                //获取服务端返回数据
                StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
                result = sr.ReadToEnd().Trim();
                sr.Close();
            }
            catch (System.Threading.ThreadAbortException e)
            {
                //Log.Error("HttpService", "Thread - caught ThreadAbortException - resetting.");
                //Log.Error("Exception message: {0}", e.Message);
                System.Threading.Thread.ResetAbort();
                throw;
            }
            finally
            {
                //关闭连接和流
                if (response != null)
                {
                    response.Close();
                }
                if (request != null)
                {
                    request.Abort();
                }
            }
            return result;
        }

    }
}